package org.snake.spring.framework.webmvc.servlet;

import org.snake.mvcframework.annotation.RAYController;
import org.snake.mvcframework.annotation.RAYRequestMapping;
import org.snake.spring.framework.context.RAYApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RAYDispatcherServlet extends HttpServlet
{

    /**
     * 应用上下文，本质上就是Spring容器的封装
     */
    private RAYApplicationContext context;

    /**
     * url映射MAP
     */
    //    private Map<String, RAYHandlerMapping> handlerMappings = new HashMap<>();
    private List<RAYHandlerMapping> handlerMappings = new ArrayList<>();

    /**
     * 缓存方法对应的参数适配器
     * 逻辑上：每一个方法，对应一个方法的参数适配器
     */
    private Map<RAYHandlerMapping, RAYHandlerAdapter> handlerAdapters = new Hashtable<>();

    /**
     * 缓存所有的视图解析器
     */
    private List<RAYViewResolver> viewResolvers = new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {

        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
    {

        try
        {
            // 运行阶段
            doDispatch(req, resp);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            // 500 异常结果
            Map<String, Object> model = new HashMap<>();
            model.put("detail", "500 Exception Detail");
            model.put("stackTrace", Arrays.toString(e.getStackTrace()));
            try
            {
                processDispatchResult(req, resp, new RAYModelAndView("500", model));
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
        }

    }

    @Override
    public void init(ServletConfig config) throws ServletException
    {

        // 实例化 RAYApplicationContext
        context = new RAYApplicationContext(config.getInitParameter("contextConfigLocation"));
        // 初始化策略
        initStrategies(context);

        System.out.println("RAY Spring is init...");
    }

    /**
     * 初始化策略-九大组件
     * <p>
     * Initialize the strategy objects that this servlet uses.
     * 初始化此servlet使用的策略对象
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     * <p>可以在子类中重写以初始化其他策略对象。</p>
     */
    protected void initStrategies(RAYApplicationContext context)
    {

        // 多文件上传的组件
        // initMultipartResolver(context);
        // 初始化本地语言环境
        // initLocaleResolver(context);
        // 初始化模板处理器
        // initThemeResolver(context);
        // 初始化HandlerMapping(保存URL和Method的关系)
        initHandlerMappings(context);
        // 初始化参数适配器(Method方法中参数的动态匹配)
        initHandlerAdapters(context);
        // 初始化异常拦截器
        // initHandlerExceptionResolvers(context);
        // 初始化视图预处理器
        // initRequestToViewNameTranslator(context);
        // 初始化视图转换器
        initViewResolvers(context);
        // 初始化闪存管理器(也叫参数缓存器)
        // initFlashMapManager(context);

    }

    /**
     * 初始化HandlerMapping(保存URL和Method的关系)
     *
     * @param context Spring上下文容器的封装
     */
    private void initHandlerMappings(RAYApplicationContext context)
    {

        if (this.context.getBeanDefinitionCount() == 0)
        {
            return;
        }

        // 获取容器中的缓存的所有BeanName
        String[] beanNames = this.context.getBeanDefinitionNames();

        for (String beanName : beanNames)
        {

            // 从IoC中获取实例对象
            Object instance = this.context.getBean(beanName);
            // 获取Class信息
            Class<?> clazz = instance.getClass();
            // 加了controller注解，才做 HandlerMapping
            if (!clazz.isAnnotationPresent(RAYController.class))
            {
                continue;
            }
            // 处理类名上的注解
            String baseUrl = "";
            if (clazz.isAnnotationPresent(RAYRequestMapping.class))
            {
                // 获取注解
                RAYRequestMapping requestMapping = clazz.getAnnotation(RAYRequestMapping.class);
                // 映射的url
                baseUrl = requestMapping.value();
            }

            // 获取所有的public方法
            for (Method method : clazz.getMethods())
            {
                if (!method.isAnnotationPresent(RAYRequestMapping.class))
                {
                    continue;
                }
                // 获取注解
                RAYRequestMapping requestMapping = method.getAnnotation(RAYRequestMapping.class);
                // 支持正则
                // 映射的url，正则的+号，表示匹配前面的子表达式一次或者多次
                String regex = ("/" + baseUrl + "/" + requestMapping.value())
                        .replaceAll("\\*", ".*")
                        .replaceAll("/+", "/");
                // 编译正则表达式
                Pattern pattern = Pattern.compile(regex);
                // 缓存RayHandlerMapping对象
                handlerMappings.add(new RAYHandlerMapping(pattern, instance, method));

                System.out.println("Mapped " + regex + "," + method);
            }
        }

    }

    /**
     * 初始化handlerAdapters，方法的参数适配器
     * 逻辑上：每一个方法，对应一个方法的参数适配器
     *
     * @param context Spring上下文容器的封装
     */
    private void initHandlerAdapters(RAYApplicationContext context)
    {

        // 循环注册handlerMapping，与handlerAdapter的一对一关系
        for (RAYHandlerMapping handlerMapping : handlerMappings)
        {
            this.handlerAdapters.put(handlerMapping, new RAYHandlerAdapter());
        }
    }

    /**
     * 初始化工程中所有的视图模板
     *
     * @param context Spring上下文容器的封装
     */
    private void initViewResolvers(RAYApplicationContext context)
    {

        String templateRoot = context.getConfiguration().getProperty("templateRoot");
        // 获取视图文件路径 eg: layouts
        String templateRootPath = Objects.requireNonNull(this.getClass().getClassLoader().getResource(templateRoot)).getFile();
        // 获取文件夹
        File templateRootDir = new File(templateRootPath);
        // 循环获取文件夹下所有的视图文件
        for (File file : Objects.requireNonNull(templateRootDir.listFiles()))
        {
            this.viewResolvers.add(new RAYViewResolver(templateRoot));
        }

    }

    /**
     * 重写 Spring 核心调度方法
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception
    {

        // 1.根据URL拿到对应的HandlerMapping
        RAYHandlerMapping handler = getHandlerMapping(req);
        // 找不到就返回404页面
        if (null == handler)
        {
            processDispatchResult(req, resp, new RAYModelAndView("404"));
            return;
        }

        // 2.根据HandlerMapping去获取到方法对应的HandlerAdapter参数适配器
        RAYHandlerAdapter handlerAdapter = getHandlerAdapter(handler);

        // 3.根据HandlerAdapter参数适配器的匹配结果，返回ModelAndView对象
        RAYModelAndView mv = handlerAdapter.handle(req, resp, handler);

        // 4.根据ModelAndView的内容，匹配对应的ViewResolver
        processDispatchResult(req, resp, mv);

    }

    /**
     * 根据URL拿到对应的HandlerMapping
     *
     * @param req HttpServletRequest对象
     * @return RAYHandlerMapping
     */
    private RAYHandlerMapping getHandlerMapping(HttpServletRequest req)
    {

        // 1. 根据请求，拿到URL
        String url = req.getRequestURI();
        // 上下文路径
        String contextPath = req.getContextPath();
        // 2. 将绝对路径，处理为相对路径
        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
        // 循环匹配
        for (RAYHandlerMapping handlerMapping : handlerMappings)
        {
            // 匹配正则
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            // 不匹配就跳过进入下一个
            if (!matcher.matches())
            {
                continue;
            }
            // 匹配的就直接返回HandlerMapping对象
            return handlerMapping;
        }
        // 都不匹配就返回null
        return null;
    }

    /**
     * 获取HandlerMapping对应的HandlerAdapter
     *
     * @param handler RAYHandlerMapping
     * @return RAYHandlerAdapter
     */
    private RAYHandlerAdapter getHandlerAdapter(RAYHandlerMapping handler)
    {

        if (this.handlerMappings.isEmpty())
        {
            return null;
        }
        // 直接从MAP中获取HandlerAdapter
        return this.handlerAdapters.get(handler);
    }

    /**
     * 处理调度最终返回结果
     *
     * @param req  HttpServletRequest
     * @param resp HttpServletResponse
     * @param mv   RAYModelAndView
     */
    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, RAYModelAndView mv) throws Exception
    {

        if(null == mv)
        {
            return;
        }

        if(this.viewResolvers.isEmpty())
        {
            return;
        }
        // 循环去尝试用模板引擎做解析
        for (RAYViewResolver viewResolver : this.viewResolvers)
        {

            RAYView view = viewResolver.resolveViewName(mv.getViewName());
            // 调用视图的渲染方法
            view.render(mv.getModel(), req, resp);

            return;
        }

    }

}
